Skip to content

feat: add HasItemThat for collections#685

Merged
vbreuss merged 2 commits intomainfrom
topic/add-hasitemthat
Jul 20, 2025
Merged

feat: add HasItemThat for collections#685
vbreuss merged 2 commits intomainfrom
topic/add-hasitemthat

Conversation

@vbreuss
Copy link
Copy Markdown
Member

@vbreuss vbreuss commented Jul 20, 2025

You can now also use expectations on the individual items.

IEnumerable<string> values = ["0th item", "1st item", "2nd item", "3rd item"];

await Expect.That(values).HasItemThat(it => it.IsEqualTo("1st item")).AtIndex(1);
await Expect.That(values).HasItemThat(it => it.StartsWith("2nd").And.EndsWith("item")).AtAnyIndex();

Note: The same expectation works also for IAsyncEnumerable<T>.

@vbreuss vbreuss self-assigned this Jul 20, 2025
Copilot AI review requested due to automatic review settings July 20, 2025 11:21
@vbreuss vbreuss added the enhancement New feature or request label Jul 20, 2025
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces the HasItemThat method for collections, enabling developers to apply expectations on individual items within collections. The feature provides a more flexible alternative to existing HasItem methods by allowing full expectation chains on collection items.

  • Adds HasItemThat methods for IEnumerable<T>, IAsyncEnumerable<T>, and ImmutableArray<T>
  • Implements comprehensive test coverage across regular, immutable, and async enumerable collections
  • Fixes a bug in AndNode/OrNode filtering to prevent circular references during expectation building

Reviewed Changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
ThatEnumerable.HasItemThat.cs Core implementation of HasItemThat for sync enumerables and immutable arrays
ThatAsyncEnumerable.HasItemThat.cs Implementation for async enumerables with proper cancellation handling
ThatEnumerable.HasItemThat.Tests.cs Comprehensive test suite covering various scenarios including null handling and error messages
ThatEnumerable.HasItemThat.ImmutableTests.cs Tests specific to ImmutableArray collections
ThatAsyncEnumerable.HasItemThat.Tests.cs Tests for async enumerable functionality
AndNode.cs / OrNode.cs Bug fix to prevent infinite loops in expectation building
API test files Updates to expected API surface for new methods
Documentation Updates with usage examples for the new feature
Comments suppressed due to low confidence (8)

Tests/aweXpect.Tests/Collections/ThatEnumerable.HasItemThat.Tests.cs:67

  • The test method name is misleading. It says 'ShouldSucceed' but the test expects the assertion to fail and throw an XunitException.
			public async Task WhenEnumerableContainsNoItemAtGivenIndex_ShouldSucceed()

Tests/aweXpect.Tests/Collections/ThatEnumerable.HasItemThat.ImmutableTests.cs:15

  • The test method name is misleading. It says 'ShouldSucceed' but the test expects the assertion to fail and throw an XunitException.
			public async Task WhenEnumerableContainsDifferentItemAtGivenIndex_ShouldSucceed()

Tests/aweXpect.Tests/Collections/ThatEnumerable.HasItemThat.ImmutableTests.cs:45

  • The test method name is misleading. It says 'ShouldSucceed' but the test expects the assertion to fail and throw an XunitException.
			public async Task WhenEnumerableContainsNoItemAtGivenIndex_ShouldSucceed()

Tests/aweXpect.Tests/Collections/ThatEnumerable.HasItemThat.Tests.cs:37

  • The test method name is misleading. It says 'ShouldSucceed' but the test expects the assertion to fail and throw an XunitException.
			public async Task WhenEnumerableContainsDifferentItemAtGivenIndex_ShouldSucceed()

Tests/aweXpect.Tests/Collections/ThatAsyncEnumerable.HasItemThat.Tests.cs:26

  • The test method name is misleading. It says 'ShouldSucceed' but the test expects the assertion to fail and throw an XunitException.
			public async Task WhenEnumerableContainsDifferentItemAtGivenIndex_ShouldSucceed()

Tests/aweXpect.Tests/Collections/ThatAsyncEnumerable.HasItemThat.Tests.cs:56

  • The test method name is misleading. It says 'ShouldSucceed' but the test expects the assertion to fail and throw an XunitException.
			public async Task WhenEnumerableContainsNoItemAtGivenIndex_ShouldSucceed()

You can now also use expectations on the individual items.

```csharp
IEnumerable<string> values = ["0th item", "1st item", "2nd item", "3rd item"];

await Expect.That(values).HasItemThat(it => it.IsEqualTo("1st item")).AtIndex(1);
await Expect.That(values).HasItemThat(it => it.StartsWith("2nd").And.EndsWith("item")).AtAnyIndex();
```

*Note: The same expectation works also for `IAsyncEnumerable<T>`.*
@vbreuss vbreuss force-pushed the topic/add-hasitemthat branch from fe24491 to baf484f Compare July 20, 2025 11:22
@vbreuss vbreuss force-pushed the topic/add-hasitemthat branch from dde49ab to d141588 Compare July 20, 2025 11:25
@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

Test Results

    4 files  ±0      4 suites  ±0   20s ⏱️ -2s
  897 tests ±0    896 ✅ ±0  1 💤 ±0  0 ❌ ±0 
2 577 runs  ±0  2 576 ✅ ±0  1 💤 ±0  0 ❌ ±0 

Results for commit d141588. ± Comparison against base commit b774dc8.

This pull request removes 54 and adds 54 tests. Note that renamed tests count towards both.
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message11b9c369-e7bc-4037-9ac8-f68ffe84da5a")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messagea21b521d-7a81-4e85-bdc9-43122c8d945b")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messagea22eefc7-6cfe-4c02-9472-47f0598f3b7a")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message025d231b-6f60-4e42-80de-60118e90d6d8")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message0a346390-db2e-4e57-b263-3127a71d8142")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "messagea7ec8314-698d-4bbb-a10e-0fa7d6412354")
aweXpect.Core.Tests.Equivalency.EquivalencyOptionsExtensionsTests ‑ Generic_For_IgnoringMember_ShouldSetOptionForType(memberToIgnore: "memberToIgnore61b923bb-cac8-4302-a0fb-f8a9090ce318")
aweXpect.Core.Tests.Equivalency.EquivalencyOptionsExtensionsTests ‑ Generic_For_IgnoringMember_ShouldSetOptionForType(memberToIgnore: "memberToIgnore737b61cc-3f8e-4c1f-b3eb-12c309921a8b")
aweXpect.Core.Tests.Equivalency.EquivalencyOptionsExtensionsTests ‑ Generic_For_IgnoringMember_ShouldSetOptionForType(memberToIgnore: "memberToIgnore93e799e7-d5d7-407a-8f1b-4542cf2c8361")
aweXpect.Core.Tests.FailTests ‑ Test_ShouldThrowException(reason: "reason791aaa94-b955-4e79-b8b4-f705a44b5576")
…
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message016120d3-9865-4ddb-bc4b-5745703fcc40")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "message77f9f3e9-72f6-46d1-89b8-7f8c76a8ce60")
aweXpect.Core.Tests.Core.Exceptions.FailExceptionTests ‑ Message_ShouldBeSet(message: "messagee7aca2d7-7f8a-4152-8f05-94f7d4e4b1d9")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message3000584e-e89b-4471-bea0-35bd0dce2e97")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "message8d0f0d3c-5ead-4820-a9b6-714eb787eac5")
aweXpect.Core.Tests.Core.Exceptions.SkipExceptionTests ‑ Message_ShouldBeSet(message: "messagecde0309a-1a83-4e61-9302-f8da491fc04d")
aweXpect.Core.Tests.Equivalency.EquivalencyOptionsExtensionsTests ‑ Generic_For_IgnoringMember_ShouldSetOptionForType(memberToIgnore: "memberToIgnore34f05e2c-0b48-4bc6-a769-6452e2c36e54")
aweXpect.Core.Tests.Equivalency.EquivalencyOptionsExtensionsTests ‑ Generic_For_IgnoringMember_ShouldSetOptionForType(memberToIgnore: "memberToIgnore4433fe78-303f-40d3-838b-6042dfc58102")
aweXpect.Core.Tests.Equivalency.EquivalencyOptionsExtensionsTests ‑ Generic_For_IgnoringMember_ShouldSetOptionForType(memberToIgnore: "memberToIgnored5644654-c048-4c36-9ef8-92eecbe994dd")
aweXpect.Core.Tests.FailTests ‑ Test_ShouldThrowException(reason: "reason25c4baba-7f27-4ea6-95b8-ea08e57b8460")
…

@vbreuss vbreuss merged commit da62c2c into main Jul 20, 2025
13 checks passed
@vbreuss vbreuss deleted the topic/add-hasitemthat branch July 20, 2025 11:34
@github-actions
Copy link
Copy Markdown
Contributor

This is addressed in release v2.20.0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request state: released The issue is released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants